
let Matrix = require('./matrix');
let Color3 = require('./color3');
let myTools = require('./utils/mytools');
let EventEmitter = require('./event_emitter');
let cfg = require('./utils/cfg');
let Task = require('./task');

class Node extends EventEmitter{
    constructor() {
        super();
        this._id = cfg.genId();
        this._gl = cfg.gl;

        this._worldMat = new Matrix();  // 总的模型矩阵
        this._modelMat = new Matrix();  // local model matrix
        this._mvpMat = new Matrix();   // mvp矩阵,需要绘制的时候才有用

        this._parent = null;
        this._children = [];

        //----------------------------------------
        // 比较通用属性
        this._x = 0;
        this._y = 0;
        this._sx = 1;
        this._sy = 1;
        this._degress = 0;
        this._zIndex = 0;
        this._ax = 0.5;
        this._ay = 0.5;

        // 不透明度
        this._opacity = 1.0;

        // color
        this._color = Color3.WHITE.clone();

        this._visible = true;
        this._width = 0;
        this._height = 0;
        //----------------------------------------

        // dirty
        this._childrenZDirty = true;
        this._bVertexDirty = true;  // 顶点数据 dirty

        /**
         * localMatDirty 会影响到 _worldMatDirty 的状态.
         * 当 localMatDirty 为 true 时, _worldMatDirty 一定为 true
         * 但是 当 _worldMatDirty 为 true时, localMatDirty 则不一定为true
         */
        this._localMatDirty = true;
        this._worldMatDirty = true;

        // TODO: opacity 和 color 的级联. 暂时不实现
        //

        // 只有加入到了场景树,才是enable
        this._bEnabled = false;
    }

    get enabled(){
        return this._bEnabled;
    }

    get id(){
        return this._id;
    }

    get width() {
        return this._width;
    }

    set width(newWidth) {
        if (this._width !== newWidth) {
            this._width = newWidth;
            this._bVertexDirty = true;
        }
    }

    get height() {
        return this._height;
    }

    set height(newHeight) {
        if (this._height !== newHeight) {
            this._height = newHeight;
            this._bVertexDirty = true;
        }
    }

    setSize(w, h) {
        if (this._width !== w || this._height !== h) {
            this._width = w;
            this._height = h;
            this._bVertexDirty = true;
        }
    }

    // isDestroyed(){
    //     return this._bDestroyed;
    // }

    get ax(){
        return this._ax;
    }

    set ax(newAx){
        if(!myTools.closeEqual(this._ax,newAx)){
            this._ax = newAx;
            this._bVertexDirty =  true;
        }
    }

    get opacity(){
        return this._opacity;
    }

    set opacity(newOpacity){
        let op = myTools.clamp(newOpacity,0,1);
        if(!myTools.closeEqual(this._opacity,op)){
            this._opacity = op;
        }
    }

    get scaledWidth() {
        return this._sx * this._width;
    }

    get scaledHeight() {
        return this._sy * this._height;
    }

    setAnchor(ax,ay){
        this.ax = ax;
        this.ay = ay;
    }

    set ay(newAy){
        if(!myTools.closeEqual(this._ay,newAy)){
            this._ay = newAy;
            this._bVertexDirty =  true;
        }
    }

    get ay(){
        return this._ay;
    }

    get color(){
        return this._color;
    }

    set color(newColor){
        this._color.set(newColor);
    }

    get x(){
        return this._x;
    }
    set x(newX){
        if(this._x !== newX){
            this._x = newX;
            this._localMatDirty = true;
            this._worldMatDirty = true;
        }
    }

    get y(){
        return this._y;
    }
    set y(newY){
        if(this._y !== newY){
            this._y = newY;
            this._localMatDirty = true;
            this._worldMatDirty = true;
        }
    }

    setPos(x,y){
        if(myTools.Types.isObject(x)){
            if(this._x !== x.x || this._y !== x.y){
                this._x = x.x;
                this._y = x.y;
                this._localMatDirty = true;
                this._worldMatDirty = true;
            }
        }else{
            if(this._x !== x || this._y !== y){
                this._x = x;
                this._y = y;
                this._localMatDirty = true;
                this._worldMatDirty = true;
            }
        }
    }

    setScale(sx,sy){
        if(myTools.Types.isObject(sx)){
            if(this._sx !== sx.x || this._sy !== sx.y){
                this._sx = sx.x;
                this._sy = sx.y;
                this._localMatDirty = true;
                this._worldMatDirty = true;
            }
        }else{
            if(this._sx !== sx || this._sy !== sy){
                this._sx = sx;
                this._sy = sy;
                this._localMatDirty = true;
                this._worldMatDirty = true;
            }
        }
    }

    get sx(){
        return this._sx;
    }
    set sx(newSX){
        if(this._sx !== newSX){
            this._sx = newSX;
            this._localMatDirty = true;
            this._worldMatDirty = true;
        }
    }
    get sy(){
        return this._sy;
    }
    set sy(newSY){
        if(this._sy !== newSY){
            this._sy = newSY;
            this._localMatDirty = true;
            this._worldMatDirty = true;
        }
    }

    get degress(){
        return this._degress;
    }

    set degress(newDegress){
        if(this._degress !== newDegress){
            this._degress = newDegress;
            this._localMatDirty = true;
            this._worldMatDirty = true;
        }
    }

    set zIndex(zIdx){
        if(this._zIndex !== zIdx){
            this._zIndex = zIdx;
            if(this.parent){
                this.parent._childrenZDirty = true;
            }
        }
    }

    get zIndex(){
        return this._zIndex;
    }

    get parent(){
        return this._parent;
    }

    get children(){
        return this._children;
    }

    get visible(){
        return this._visible;
    }

    set visible(value){
        if(this._visible !== value){
            this._visible = value;
        }
    }

    //----------------------------------------
    addChild(node){
        if(this._children.indexOf(node) >= 0){
            throw new Error('node already in children');
        }
        if(node.parent){
            throw new Error('node already has parent!');
        }

        node._parent = this;
        this._children.push(node);
        this._childrenZDirty = true;

        node.onEnable();
    }

    onEnable(){
        this._bEnabled = true;
    }

    destroy(){
        if(!this._bEnabled){
            console.warn(`already removed!`);
            return;
        }

        if(this._parent){
            this._parent._removeChild(this);
        }else{
            throw new Error('还未加入场景树');
        }
    }

    // 删除所有子节点
    removeAllChildren(){
        let len = this._children.length;
        for(let i = 0; i < len; ++i){
            this._children[i].removeAllChildren();
            this._children[i].dispose();
        }
        this._children.length = 0;
    }

    dispose(){
        this._parent = null;
        this._bEnabled = false;

        cfg.gameApp.actionMgr.removeAllActionOnTarget(this);
        cfg.gameApp.scheduler.removeAllTargetByNode(this);
    }

    _removeChild(node){
        let idx = this._children.indexOf(node);
        if(idx >= 0){
            // 从节点中直接删除
            this._children.splice(idx,1);

            // 删除所有子节点,再删除自己
            node.removeAllChildren();
            node.dispose();
        }else{
            throw new Error('node do not in children');
        }
    }

    _visitForUpdate(lstUpdate){
        if(this.visible){
            lstUpdate.push(this);

            this._children.forEach(child=>{
                child._visitForUpdate(lstUpdate);
            });
        }
    }

    _visitForRender(lstRender){

        if(!this.visible){
            return;
        }

        // 判断自己的数据是否发生了改变
        /**
         * posDirty
         * scaleDirty
         * degressDirty
         * sizeDirty
         */
        if(this._bVertexDirty){
            this.onVerticesChanged();
            this._bVertexDirty = false;
        }

        let renderItem = this.getRenderItem();
        if(renderItem){
            lstRender.push(renderItem);
        }

        // 子节点可能需要重排
        if(this._childrenZDirty){
            this._sortByZ();
            this._childrenZDirty = false;
        }

        this._children.forEach(child=>{
            child._visitForRender(lstRender);
        });
    }


    //---------------------------------------------
    // override
    // 提取一个可渲染的数据
    getRenderItem(){
        return null;
    }
    update(){}

    // override
    onVerticesChanged(){}

    /**
     * 要求稳定排序
     * @private
     */
    _sortByZ(){
        let arr = this._children;
        let len = arr.length;
        for(let i = 1; i < len ; i++) {
            let tmpNode = arr[i];
            for (let j = i; j > 0 && arr[j - 1]._zIndex > tmpNode._zIndex; j--) {
                arr[j] = arr[j - 1];
                arr[j - 1] = tmpNode;
            }
        }
    }


    //---------------------------------------
    // action
    runAction(action){
        if(!action){
            throw new Error('action is null!');
        }
        action.target = this;
        cfg.gameApp.actionMgr.addAction(action);
    }

    schedule(func,delay){
        delay = delay || 0;
        let task = new Task(this,func,delay);
        cfg.gameApp.scheduler.addTask(task);
    }

    scheduleOnce(func,delay){
        delay = delay || 0;
        let task = new Task(this,func,delay,true);
        cfg.gameApp.scheduler.addTask(task);
    }

    unschedule(func){
        let scheduler = cfg.gameApp.scheduler;
        let task = scheduler.getTask(this,func);
        if(task){
            scheduler.removeTask(task);
        }
    }

    /**
     * 将此节点下的 localPos 转换到 世界坐标系下
     * @param localPos
     * @return {Vec2}
     */
    convertToWorldPos(localPos){
        this._updateWorldMatrix();
         return this._worldMat.translatePoint2(localPos.x,localPos.y);
    }

    /**
     * 将世界坐标系 转换到 当前坐标系下
     * @param worldPos
     * @returns {Vec2}
     */
    convertToNodePos(worldPos){
        this._updateWorldMatrix();
        let invMat = this._worldMat.getInverse();
        return invMat.translatePoint2(worldPos.x,worldPos.y);
    }

    /**
     * 更新当前节点的 worldMat
     */
    _updateWorldMatrix(){
        if(this._parent){
            this._parent._updateWorldMatrix();
        }
        this._updateMatrix();
    }

    _updateMatrix(){
        if(this._localMatDirty){
            this._modelMat.buildFromNode2(this);
            this._localMatDirty = false;
        }
        if(this._worldMatDirty){
            if(this._parent){
                Matrix.mul(this._worldMat,this._parent._worldMat,this._modelMat);
            }else{
                this._worldMat.copy(this._modelMat);
            }
            this._worldMatDirty = false;
        }
    }


    //--------------------------------------
    // matrix update
    _updateSceneGraphMatrix(){

        if(!this.visible){
            return;
        }

        // 更新dirty标志
        if( this._worldMatDirty){
            // 所有的子节点的 worldMatDirty 都要更新
            this.children.forEach(child=>{
                child._worldMatDirty = true;
            });
        }

        this._updateMatrix();

        this.children.forEach(child=>{
            child._updateSceneGraphMatrix();
        });
    }



}

module.exports = Node;
